package edu.gatech.ors.mrsim.io.conf;

import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import edu.gatech.ors.mrsim.exceptions.WrongClassTypeException;

/**
 * A basic configuration interface. All configurations are fundamnetally stored as key-value pairs, and are required to at
 * minimum provide the additional functionality of being able to declare some keys final(not mutable) and having an being
 * able to ignore capitalization for keys.
 * 
 * @author Andrey Kurenkov
 * 
 */
public interface Configuration {
	public final static String LIST_SEPERATOR = ",";
	public final static String FINAL = "FINAL";
	public final static String IGNORE_CASE = "FINAL,IGNORE_CASE";
	public final static String[] RESERVED = { FINAL, IGNORE_CASE };

	/**
	 * A simple set method for a string key-value pair.
	 * 
	 * @param key
	 *            the key of the key-value pair
	 * @param value
	 *            the value of the key-value pair
	 * @return null if key is null or key in RESERVED or key in value of FINAL, otherwise result from set method
	 */
	public String setString(String key, String value);

	/**
	 * Overriden getProperty method allowing for ignore case.
	 * 
	 * @param key
	 *            the key to retrieve
	 * @return String that results from retrieval
	 */
	public String getString(String key);

	/**
	 * A method to set a File association with this Configuration. This method along with save and reload are the sole
	 * interface to doing IO with a Configuration.
	 * 
	 * @param setTo
	 *            the file to set it to
	 */
	public void setFile(File setTo);

	/**
	 * A method to get ther File association with this Configuration(set through setFile or constructor);
	 * 
	 * @return file
	 */
	public File getFile();

	/**
	 * Allows programmers to disable or enable additional logic of Configuration (checking for final keys and treating keys
	 * while ignoring capitalization). Results in small O(1) potential speedup if many retrievals are continually needed. By
	 * default feutures are enabled.
	 * 
	 * @param setTo
	 *            whether to disable or enable configurations
	 */
	public void setAdditionalLogicEnabled(boolean setTo);

	/**
	 * Returns all keys set to be final through setKeyFinal (which stores a list of keys as the value for the FINAL key).
	 * 
	 * @return List of keys set to be final either in file or through setKeyFinal
	 */
	public List<String> getFinalKeys();

	/**
	 * Given the key, sets it to be in the list of final keys in the value associated with the FINAL key.
	 * 
	 * @param key
	 *            the key to set final
	 * @return false if key already final, else true
	 */
	public boolean setKeyFinal(String key);

	/**
	 * Checks if ignoreCase is set and returns value if it is
	 * 
	 * @return value of ignore case property as boolean, or false if it is not set or wrong format
	 */
	public void setIgnoreCase(boolean setTo);

	/**
	 * Checks if ignoreCase is set and returns value if it is
	 * 
	 * @return value of ignore case property as boolean, or false if it is not set or wrong format
	 */
	public boolean isIgnoreCase();

	/**
	 * Given the key, if the key is final(in the list of final keys in the value associated with the FINAL key), it is
	 * removed from there and made mutable. Otherwise does nothing.
	 * 
	 * @param key
	 *            the key to set mutable
	 * @return false if key was not final, else true
	 */
	public boolean setKeyMutable(String key);

	/**
	 * Retrieves a boolean value associated with the given key. Boolean values are stored with the toString of the boolean
	 * class and parsed by that class. If the key is not found this returns null (thus the Boolean return type).
	 * 
	 * @param key
	 *            the key to retrieve the value for
	 * @param defaultValue
	 *            the default value to return
	 * @return
	 */
	public Boolean getBoolean(String key);

	/**
	 * Utility method to get property parsed to a list of booleans.
	 * 
	 * @param key
	 *            key to get values for
	 * @return the boolean values if right format and key contained, null otherwise
	 */
	public boolean[] getBooleans(String key);

	/**
	 * Loads a class from one key in the configuration.
	 * 
	 * @param key
	 *            the key of the class
	 * @return The succesfully loaded class, or null on error.
	 * @throws ClassNotFoundException
	 */
	public Class<?> getClass(String key) throws ClassNotFoundException;

	/**
	 * Loads a class from one key in the configuration.
	 * 
	 * @param key
	 *            the key of the class
	 * @param loader
	 *            the method of loading to use with the class
	 * @return The succesfully loaded class, or null on error.
	 * @throws ClassNotFoundException
	 */
	public Class<?> getClass(String key, ConfigurationClassLoader loader) throws ClassNotFoundException;

	/**
	 * Loads a list of classes from one key in the configuration.
	 * 
	 * @param key
	 *            the key of the list of classes
	 * @return The succesfully loaded classes, or null on error.
	 * @throws ClassNotFoundException
	 */
	public Class<?>[] getClasses(String key) throws ClassNotFoundException;

	/**
	 * Loads a list of classes from one key in the configuration.
	 * 
	 * @param key
	 *            the key of the list of classes
	 * @param loader
	 *            the method of loading to use with all the classes
	 * @return The succesfully loaded classes, or null on error.
	 * @throws ClassNotFoundException
	 */
	public Class<?>[] getClasses(String key, ConfigurationClassLoader loader) throws ClassNotFoundException;

	/**
	 * Utility method to get a property as a File.
	 * 
	 * @param key
	 *            the key of the property
	 * @return null if key not contained, or file with the path as the valueif it is contained
	 */
	public File getFile(String key);

	/**
	 * Utility method to get property parsed to a double.
	 * 
	 * @param key
	 *            key to get value for
	 * @return the double value if right format and key contained, null otherwise
	 */
	public Double getDouble(String key);

	/**
	 * Utility method to get property parsed to a list of doubles.
	 * 
	 * @param key
	 *            key to get values for
	 * @return the double values if right format and key contained, null otherwise
	 */
	public double[] getDoubles(String key);

	/**
	 * Utility method to get property parsed to an int.
	 * 
	 * @param key
	 *            key to get value for
	 * @return the int value if right format and key contained, null otherwise
	 */
	public Integer getInt(String key);

	/**
	 * Utility method to get property parsed to a list of ints.
	 * 
	 * @param key
	 *            key to get values for
	 * @return the int values if right format and key contained, null otherwise
	 */
	public int[] getInts(String key);

	/**
	 * Utility method to get property parsed to a long.
	 * 
	 * @param key
	 *            key to get value for
	 * @return the long value if right format and key contained, false otherwise
	 */
	public Long getLong(String key);

	/**
	 * Utility method to get property parsed to a list of longs.
	 * 
	 * @param key
	 *            key to get values for
	 * @return the long values if right format and key contained, null otherwise
	 */
	public long[] getLongs(String key);

	/**
	 * If key is associated with a series of LIST_SEPERATOR seperated (used by setStrings method), this retrieves an array of
	 * those Strings.
	 * 
	 * @param key
	 *            the key to get strings for
	 * @return an array of Strings, or null if the key is invalid
	 */
	public String[] getStrings(String key);

	/**
	 * Reloads the configuration from the currently associated propertyFile.
	 * 
	 * @return true if sucessful, false of file not set or that is an error
	 */
	public boolean reloadConfiguration();

	/**
	 * Saves the configuration to its current file if the file exists and there are no errors.
	 * 
	 * @return true if sucessful, false of file not set or that is an error
	 */
	public boolean saveConfiguration();

	/**
	 * Saves the configuration to its current file, or create
	 * 
	 * @param create
	 *            a boolean to indicate if the file should be created if it does not exist
	 * @return true if sucessful, false of file not set or that is an error
	 */
	public boolean saveConfiguration(boolean create);

	/**
	 * Associates the given key with a string representation of a boolean
	 * 
	 * @param key
	 *            the key to store the boolean with
	 * @param value
	 *            the boolean value to convert to a string
	 * @return The string value the key was associated to, or null if key is null.
	 */
	public String setBoolean(String key, boolean value);

	/**
	 * Associates the given key with a string representation of a list of booleans
	 * 
	 * @param key
	 *            the key to store the booleans with
	 * @param value
	 *            the boolean values to convert to a string
	 * @return The string value the key was associated to, or null if key is null.
	 */
	public String setBooleans(String key, Boolean... values);

	/**
	 * Set the value of the name property to the name of a theClass implementing the given interface interface. An exception
	 * is thrown if theClass does not implement the interface.
	 * 
	 * @param key
	 *            the key of the class type
	 * @param theClass
	 *            The class to save
	 * @param superType
	 *            the superinterface or superclass type that needs to be enforced
	 * @throws WrongClassTypeException
	 * @return the String value of what the key was associated to, or null if key is null.
	 */
	public String setClass(String key, Class<?> theClass, Class<?> superType) throws WrongClassTypeException;

	/**
	 * Set the value of the name property to the name of a theClas.
	 * 
	 * @param key
	 *            the key of the class type
	 * @param theClass
	 *            The class to save
	 * @return the String value of what the key was associated to, or null if key is null.
	 */
	public String setClass(String key, Class<?> theClass);

	/**
	 * Saves a list of classes to one key in the configuration. The classes are stored by their getName() value and can be
	 * retrieved through getClasses.
	 * 
	 * @param key
	 *            the key of the list of classes
	 * @param classes
	 *            The classes to save
	 * @return the String value of what the key was associated to, or null if key is null.
	 */
	public String setClasses(String key, Class<?>... classes);

	/**
	 * Sets the value associated with the key to the value if it is not already set.
	 * 
	 * @param key
	 *            the key
	 * @param value
	 *            the value
	 * @return true if the value was set, false otherwise
	 */
	public boolean setIfUnset(String key, String value);

	/**
	 * Sets the key to the String value of the int.
	 * 
	 * @param key
	 * @param value
	 * @return the String value of the int stored, or null if key is null.
	 */
	public String setInt(String key, int value);

	/**
	 * Sets the key to the String value of the this list of ints. Retrieved with getInts.
	 * 
	 * @param key
	 * @param value
	 * @return the String value of the ints stored, or null if key is null.
	 */
	public String setInts(String key, Integer... values);

	/**
	 * Sets the key to the String value of the long.
	 * 
	 * @param key
	 * @param value
	 * @return the String value of the long stored, or null if key is null.
	 */
	public String setLong(String key, long value);

	/**
	 * Sets the key to the String value of the this list of longs. Retrieved with getLongs.
	 * 
	 * @param key
	 * @param value
	 * @return the String value of the longs stored, or null if key is null.
	 */
	public String setLongs(String key, Long... values);

	/**
	 * Sets the key to the String value of the double.
	 * 
	 * @param key
	 * @param value
	 * @return the String value of the double stored, or null if key is null.
	 */
	public String setDouble(String key, long value);

	/**
	 * Sets the key to the String value of the this list of doubles. Retrieved with getDoubles.
	 * 
	 * @param key
	 * @param value
	 * @return the String value of the doubles stored, or null if key is null.
	 */
	public String setDoubles(String key, Double... values);

	/**
	 * Associates the given strings with the key using the given seperator. The strings are stored simply as a
	 * seperator-seperated string combination of all the individual strings. The getStrings method can be used to retrieve an
	 * array of these strings.
	 * 
	 * @param key
	 *            the key
	 * @param seperator
	 *            the seperator String
	 * @param values
	 *            the Strings to associate the key
	 * @return the String that the key is now associated with, or null if key is null.
	 */
	public String setStrings(String key, String seperator, String... values);

	/**
	 * Associates the given strings with the key using a default seperator string. The strings are stored simply as a
	 * LIST_SEPERATOR-seperated string combination of all the individual strings. The getStrings method can be used to
	 * retrieve an array of these strings, but will work wrongly if any of the values strings have LIST_SEPERATOR in them.
	 * 
	 * @param key
	 *            the key
	 * @param values
	 *            the String to associate the key
	 * @return the String that the key is now associated with, or null if key is null.
	 */
	public String setStrings(String key, String... values);

	/**
	 * Associates the given objects' toString() with the key using a default seperator string. The toStrings are stored
	 * simply as a LIST_SEPERATOR-seperated string combination of all the individual strings. The getStrings method can be
	 * used to retrieve an array of these strings, but will work wrongly if any of the values strings have LIST_SEPERATOR in
	 * them.
	 * 
	 * @param key
	 *            the key
	 * @param values
	 *            the String to associate the key
	 * @return the String that the key is now associated with, or null if key is null.
	 */
	public String setObjectsAsStrings(String key, Object... values);

	/**
	 * Associates the given objects' toString() values with the key using the given seperator. The toStrings are stored
	 * simply as a seperator-seperated string combination of all the individual strings. The getStrings method can be used to
	 * retrieve an array of these strings.
	 * 
	 * @param key
	 *            the key
	 * @param seperator
	 *            the seperator String
	 * @param values
	 *            the Objects to associate the key
	 * @return the String that the key is now associated with, or null if key is null.
	 */
	public String setObjectsAsStrings(String key, String seperator, Object... values);

	/**
	 * Checks if configuration has key
	 * 
	 * @param key
	 *            key to check for
	 * @return true if has key, false otherwise
	 */
	public boolean containsKey(String key);

	/**
	 * Checks if configuration has value
	 * 
	 * @param value
	 *            value to check for
	 * @return true if has value, false otherwise
	 */
	public boolean containsValue(Object value);

	/**
	 * Check if configuration is empty
	 * 
	 * @return if storing no values, then true. False otherwise.
	 */
	public boolean isEmpty();

	/**
	 * Getter for all the keys stored in this Configuration
	 * 
	 * @return Set of keys
	 */
	public Set<String> getKeys();

	/**
	 * Getter for all the objects stored in this Configuration
	 * 
	 * @return Collection of objects
	 */
	public Collection<Object> getValues();

	/**
	 * Convenience method to extract a subset of this configuration for the given keys.
	 * 
	 * @return Configuration with all key-value pairs for which corresponding entires existed in this one
	 * @param keys
	 * @return
	 */
	public Configuration getSubset(String[] keys);

}